﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.XPath;
using System.Xml;
using FinanSoft.Entities;
using FinanSoft.Utilities;
using System.Collections.ObjectModel;
using FinanSoft.DataLayer;

namespace FinanSoft.BusinessLayer
{
    public class CatalogManager
    {
        #region Atributos
        /// <summary>
        /// Única instancia de esta clase
        /// </summary>
        private static CatalogManager _instance;
        private AccountingCatalog _AccountingCatalog;
        private bool _IsError;
        private String _ErrorMessage;
        #endregion

        #region Constructor singleton
        private CatalogManager()
        {
            _IsError = false;
            _ErrorMessage = "";
            _AccountingCatalog = CatalogDataManager.GetInstance().GetAccountingCatalog();
            if (_AccountingCatalog == null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_ACCOUNTING_CATALOG_READING;
            }
            else
            {
                _IsError = false;
            }
        }
        

        /// <summary>
        /// Método para implementar el patrón de Singleton
        /// </summary>
        /// <returns>Instancia única de la clase</returns>
        public static CatalogManager GetInstance()
        {
            if (_instance == null)
                _instance = new CatalogManager();
            return _instance;
        }
        #endregion

        #region Propiedades
        public bool IsError
        {
            get
            {
                bool temp = _IsError;
                _IsError = false;
                return temp;
            }
        }

        public String ErrorMessage
        {
            get
            {
                String temp = _ErrorMessage;
                _IsError = false;
                _ErrorMessage = "";
                return temp;
            }
        }
        #endregion

        #region Métodos públicos
        /// <summary>
        /// Método que se encarga de crear una nueva cuenta en el padre indicado
        /// </summary>
        /// <param name="account">Cuenta a agregar</param>
        /// <param name="fatherID">id del padre cuenta</param>
        /// <returns>Retorna booleano de éxito o no</returns>
        public bool CreateNewAccount(Account account, int fatherID)
        {
            // Se revisa que la cuenta a agregar no tenga el mismo código que las ya agregadas
            // en el catálogo contable
            account.Children = new Dictionary<int, Account>();
            if (GetAccount(account.AccountCode) != null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_ACCOUNT_CODE_FAILED;
                return false;
            }
            // Se agrega la cuenta al catálogo y se revisa si no ocurrió algún error
            bool noErrorAC = _AccountingCatalog.AddNewAccount(account, fatherID);
            if (!noErrorAC)
            {
                _IsError = true;
                _ErrorMessage = _AccountingCatalog.MessageError;
                return false;
            }
            // Se agrega la cuenta a la base de datos y se revisa si no hubo algún error
            int noErrorDB = CatalogDataManager.GetInstance().CreateNewAccount(account, fatherID);
            if(noErrorDB == -1)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_NEW_ACCOUNT_FAILED;
                _AccountingCatalog.DeleteAccount(account.AccountID);
                return false; 
            }
            _AccountingCatalog.DeleteAccount(0);
            account.AccountID = noErrorDB;
            _AccountingCatalog.AddNewAccount(account, fatherID);
            return true;
        }

        /// <summary>
        /// Método que se encarga de eliminar una cuenta
        /// </summary>
        /// <param name="accountID">id de la cuenta a eliminar</param>
        /// <returns>Retorna el booleano de éxito o no</returns>
        public bool DeleteAccount(int accountID)
        {
            Account account = GetAccount(accountID);
            int fatherID = Constants.INVALID_ID;
            if (account.Parent != null)
                fatherID = account.Parent.AccountID;
            // Se agrega la cuenta al catálogo y se revisa si no ocurrió algún error
            bool noErrorAC = _AccountingCatalog.DeleteAccount(accountID);
            if (!noErrorAC)
            {
                _IsError = true;
                _ErrorMessage = _AccountingCatalog.MessageError;
                return false;
            }
            // Se elimina la cuenta en la base de datos y se revisa que no haya error, 
            // de ser así se deshacen los cambios...
            bool noErrorDB = CatalogDataManager.GetInstance().DeleteAccount(accountID);
            if (!noErrorDB)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_NEW_ACCOUNT_FAILED;
                _AccountingCatalog.AddNewAccount(account, fatherID);
                return false;
            }
            return true;
        }

        /// <summary>
        /// Método que se encarga de modidicar la cuenta especificada
        /// </summary>
        /// <param name="account">cuenta a modificar</param>
        /// <returns>Retorna booleano de éxito o no</returns>
        public bool ModifyAccount(Account account)
        {
            Account originalAccount = this.GetAccount(account.AccountID);
            // Se modifica la cuenta en el catálogo y se revisa que no haya habido errores
            bool noErrorAC = _AccountingCatalog.EditAccount(account);
            if (!noErrorAC)
            {
                _IsError = true;
                _ErrorMessage = _AccountingCatalog.MessageError;
                return false;
            }
            // Se modifica la cuenta en la base de datos y se revisa que no haya habido errores
            // y si los hubo modifica la cuenta a la original
            bool noError = CatalogDataManager.GetInstance().ModifyAccount(originalAccount);
            if (!noError)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_EDIT_ACCOUNT_FAILED;
                _AccountingCatalog.EditAccount(originalAccount);
                return false;
            }
            return true;
        }

        /// <summary>
        /// Método que se encarga de inicializar el catálogo contable
        /// </summary>
        /// <returns>Retorna el catálogo contable con todas la cuentas</returns>
        public AccountingCatalog GetAccountingCatalog()
        {
            if (_AccountingCatalog == null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_ACCOUNTING_CATALOG_READING;
            }
            
            return _AccountingCatalog;
        }

        /// <summary>
        /// Busca una cuenta con el Id dado en el catálogo de cuentas
        /// Si no existe entonces devuelve nulo
        /// </summary>
        /// <param name="id">Id de la cuenta que se busca</param>
        /// <returns>Cuenta asociada al Id, nulo si no existe</returns>
        public Account GetAccount(int id)
        {
            foreach (Account mainAccount in _AccountingCatalog.PrincipalAccounts)
            {
                Account possibleAccount = mainAccount.GetAccount(id);
                if (possibleAccount != null)
                    return possibleAccount;
            }
            return null;
        }

        /// <summary>
        /// Busca una cuenta con el codigo dado en el catálogo de cuentas
        /// Si no existe entonces devuelve nulo
        /// </summary>
        /// <param name="id">codigo de la cuenta que se busca</param>
        /// <returns>Cuenta asociada al codigo, nulo si no existe</returns>
        public Account GetAccount(String code)
        {
            foreach (Account mainAccount in _AccountingCatalog.PrincipalAccounts)
            {
                Account possibleAccount = mainAccount.GetAccount(code);
                if (possibleAccount != null)
                    return possibleAccount;
            }
            return null;
        }

        /// <summary>
        /// Refresca el catálogo contable
        /// </summary>
        public void Upload()
        {
            _AccountingCatalog = CatalogDataManager.GetInstance().GetAccountingCatalog();
            if (_AccountingCatalog == null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_ACCOUNTING_CATALOG_READING;
            }
            else
            {
                _IsError = false;
            }
        }
        #endregion

    }
}
